#pragma once
#include "AppDelegate.h"
#include "cocos2d.h"
#include "debug.h"
#include "NoteManager.h"
#include "HighPrecisionTimer.h"
#include "HighPrecisionInput.h"
#include "UIPlaySceneFallingSolo.h"

#define SHOWKEYOFFSET false
#define TYPE_OSU 1
#define TYPE_BMS 2

USING_NS_CC;

class ControllerFallingMode
{
	public:
	//type: 1=osufile, 2=bmsfile.
	ControllerFallingMode(string noteFilePath, float hispeed, int filetype = 1);
	~ControllerFallingMode() {};


	public:
	int type_file;
	int type_key;
	UIFallingModePanel* pPanelFrame;
	Node* pNoteField;
	Node* pHud;
	UIFallingModeHitJudgment* pHitJudgment;
	Node* pLayerAbility;
	Label* pLabelAbility;
	Node* pLayerGraph;
	Node** pOffsetGraph;
	float graphRowHeight;
	Node** pTrackLights;
	Node** pKeyButtons;
	float singleNoteHeight = 10;
	float baseSpeed = 2.0f;
	float changeSpeed = 0;
	bool isPaused = true;
	LARGE_INTEGER cpuTick, cpuFreq;
	HighPrecisionTimer* hTimer;
	HighPrecisionInput* hInput;
	bool exit = false;

	#define OFF_PGREAT 30
	#define OFF_GREAT 60
	#define OFF_GOOD 90
	#define OFF_POOR 150
	#define OFF_BREAK 300

	#define HIT_PGREAT 0
	#define HIT_GREAT 1
	#define HIT_GOOD 2
	#define HIT_POOR  3
	#define HIT_BREAK 4

	const Color3B COLOR_HIT_RANGE[5] =
	{
		Color3B(224, 81, 202),
		Color3B(224, 179, 81),
		Color3B(108, 224, 81),
		Color3B(67, 225, 130),
		Color3B(255, 0, 0)
	};
	const Color3B COLOR_PGREAT = COLOR_HIT_RANGE[0];
	const Color3B COLOR_GREAT = COLOR_HIT_RANGE[1];
	const Color3B COLOR_GOOD = COLOR_HIT_RANGE[2];
	const Color3B COLOR_POOR = COLOR_HIT_RANGE[3];
	const Color3B COLOR_BREAK = COLOR_HIT_RANGE[4];

	public:  //Set private when debug finish
	float noteFieldHeight;
	float trackWidth;
	float trackWidthS;
	int trackCount;
	unsigned int* trackNoteIndex;
	unsigned int bgmLength;
	bool isSoundsLoaded = false;
	bool bgmStarted = false;
	bool bgmIsPausedPrev = false;
	float curTime = 0;
	float topTime;
	int dspClockInit;
	float deltaTime = 0;
	float deltaTimeBMS = 0;
	unsigned int prevBgmTime = 0;
	float prevGameTime = 0; // Only update when bgm channel buffer refreshed.
	int audioBufferFrames = 1;
	int audioBufferCount = 0;
	int totalFrames = 0;
	float avOffset = 0;
	float avOffsetPerFrame = 0;
	float deltaTimeMod = 1;

	struct TimeSegPara
	{
		int startTime;
		float noteSpeedPerMs; // GLPoint per ms
		float timeFlowSpeedPerGLP; // ms per GLPoint
	};
	TimeSegPara* pTimeSegPara;
	int timeSegIndex = 0;

	struct NoteObj
	{
		bool hitting = false;
		bool resized = false;
		float pressTime = 0;
		int hitLevel = 9;
		//bool canAutoPlay = true;
		Node* obj;
		NoteManager::Note info;
	};
	vector<NoteObj>* notesInTrack;

	struct TrackStatus
	{
		bool isPressed;
		bool isReleaseNextFrame;
		float pressTime;
	};
	TrackStatus* trackStatus;
	TrackStatus* trackLastStatus;

	struct HitEffect
	{
		int trackIndex;
		int hitRange;
	};
	vector<HitEffect> hitEffectPool;

	struct HitLevel
	{
		int hitLevel;
		float offset;
	};
	vector<HitLevel> hitLevelPool;
	vector<int> hitSoundPool;

	bool isAutoPlay = false;
	bool lastAutoPlayStat = false;
	int totalNotesCount = 0;
	int curCombo = 0;
	int maxCombo = 0;
	float totalAcc = 0;
	float avgAcc;
	int hitLevelCount[5] = { 0,0,0,0,0 };
	int totalHitCount = 0;
	float totalHitOffset = 0;
	float avgHitOffset;
	int notePlayed = 0;
	int graphRows;
	int graphInterval;
	int* graphOffsetCount;
	bool isThreadStart = false;

	public:
	void setSceneObject(Node* layerHUD, Node* panelFrame);
	void setTimeSegPara();
	void updateController(bool includeAudio);
	void checkNormalInput(CCKEYCODE keycode, bool isPressed);

	void checkHPInput();

	void playHitSound();

	void playBgmChannel();

	int getTrackCount();
	int getTimeSeg(int timePoint);

	private:
	void updateAudio();
	void setCurTime(float dt);
	void setCurTimeHighPrecision();
	void setCurTimeHighPrecisionBMS();
	float getGameTimeHighPrecision();
	void moveNotesInField();
	void setTopTime();
	void drawNewNotes();
	float getNoteOffsetY(int timePoint);
	void removeOldNotes();
	void deleteNote(int trackIndex, vector<NoteObj>::iterator noteIter);
	void changeHispeed();


	void keyPress(int trackIndex);
	void keyPress(int trackIndex, float keyTime);

	void keyRelease(int trackIndex);
	void keyReleaseNextFrame(int trackIndex);

	void updateKeyEffect();

	void showHitEffect();

	void checkHitRange(int trackIndex, float keyTime);

	void checkNoteHit();
	void showHitLevel();
	void updateScore();
	void updateOffsetGraph();

	void autoPlay();

	//***Multi-threads functions
	void loadSounds();
	void startThreads();
};